

// version4在version3的基础上, 将设置 全局target并触发getter 更让转进Watcher类中，让Deps的队列中转入Watcher（更有语义化）
// 封装 Watcher 类（就是Window.target收集的对象, window.target 从哪里来呢？从watcher中来）


class Watcher {
    constructor(vm,exression,callback){
        this.vm = vm; // vue对象, 也就是defineReact 产生的对象
        this.getter = getParsePathFunc(exression);

        this.callback = callback;

        this.value = this.get(); // 当前的值，立刻调用getter
    }

    get(){
        Dep.target = this; // 先将 要收集的全局变量 存起来， this就是这个watcher实例
        let val = this.getter.call(this.vm, this.vm); // 调用getter，也就是调用 vm.a.b.c之类的getter（vm就是被defineReactive调用的对象），将此watcher实例加入Deps的主题队列
        Dep.target = undefined; // 删除 存在的依赖
        return val;
    }

    update(){
        const oldVal = this.value;
        this.value = this.get(); // 获取新的值
        this.callback.call(this.vm, this.value, oldVal); // 监听的回调
    }
}


class Dep{

    static target = null; // 书中写为window.target 这里写为 Dep.target

    constructor(){
        this.subs = []; //订购的主题（订阅者模式），也就是回调函数 队列
    }

    addSub(sub){ // 这里的sub就是watcher实例
        this.subs.push(sub);
    }

    removeSub(sub){
        if(this.subs.length){
            let deleteIndex = this.subs.findIndex(item=>item==sub);
            if(deleteIndex>-1){
                return this.subs.splice(deleteIndex,1);
            }
        }
    }

    // 获取当前 依赖、回调
    depend(){
        if(Dep.target){
            this.addSub(Dep.target) 
        }
    }

    //  触发此属性的修改 回调队列，调用每一个watcher的回调
    notify(val){
        const subs = this.subs.slice(0);
        for(let i=0;i<subs.length;i++){
            subs[i].update(val);
        }
    }
}

// 通过路径 'a.b.c' 获得 obj.a.b.c 的函数
function getParsePathFunc(path){
    const pathReg = /(\w+\.?\w+)+/;
    if(!pathReg.test(path)){
        console.error('非法路径');
        return
    }

    const arr = path.split('.');
    return function(obj){
        let resultObj = obj;
        for(let pathItem of arr){
            if(resultObj){
                resultObj = resultObj[pathItem];
            }else{
                console.error('路径的属性不存在');
                return
            }
        }

        return resultObj;
    }
}

function defineReactive(obj={}, key='',val){
    const dep = new Dep();
    Object.defineProperty(
        obj,
        key,
        {
            enumerable: true,
            configurable: true,
            get: function(){

                dep.depend();
                Dep.target = undefined;

                return val;             
            },
            set: function(newVal){
                if(val==newVal){
                    return;
                }                
                val = newVal;
                dep.notify(newVal);
            }
        }
    )

    return obj;
}


const testObj = { name:'123', age:18, inner: { text:'打完等我回家' } };

defineReactive(testObj, 'name', testObj.name); //先构建vm对象，此时Dep.target 不存在, 修改不会触发回调
defineReactive(testObj.inner, 'text', testObj.inner.text);

testObj.name = '不知u是';


const watch1 = new Watcher(testObj,'name',function(newVal,oldVal){
    console.log(`name 属性值改变，原本为: ${oldVal}, 新值为: ${newVal}`);
});

const watch2 = new Watcher(testObj,'inner.text',function(newVal,oldVal){
    console.log(`inner.text 属性值改变，原本为: ${oldVal}, 新值为: ${newVal}`);
});

testObj.name = '不得不结婚后就wqdw';

testObj.inner.text = '的那位考勤机电脑网卡驱动框架 你看11w';

